package com.iteaj.iot.serial;

import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.iteaj.iot.client.ClientComponent;
import com.iteaj.iot.client.IotClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.function.Consumer;

public class SerialClient implements IotClient<Object> {

    private SerialPort serialPort;
    private SerialPortDataListener listener;
    private SerialComponent serialComponent;
    private SerialConnectProperties connectProperties;
    private Logger logger = LoggerFactory.getLogger(getClass());

    public SerialClient(SerialComponent serialComponent, SerialConnectProperties connectProperties) {
        this.serialComponent = serialComponent;
        this.connectProperties = connectProperties;
    }

    public boolean isOpen() {
        return this.serialPort.isOpen();
    }

    @Override
    public int getPort() {
        throw new UnsupportedOperationException("不支持的操作");
    }

    @Override
    public String getHost() {
        return this.connectProperties.getHost();
    }

    @Override
    public void init(Object arg) {
        this.serialPort = SerialPort.getCommPort(this.connectProperties.connectKey());
        this.serialPort.setComPortParameters(this.connectProperties.getBaudRate()
                , this.connectProperties.getDataBits(), this.connectProperties.getStopBits()
                , this.connectProperties.getParity(), this.connectProperties.isUseRS485Mode());
    }

    /**
     * 打开串口
     * @return
     */
    public boolean open() {
        return this.connect(null, 0);
    }

    /**
     * 当前串口可读的字节数
     * @return 当前可读取的字节数, 如果端口未打开则为-1
     */
    public int bytesAvailable() {
        return this.serialPort.bytesAvailable();
    }

    /**
     * @return 返回仍在等待写入设备输出的字节数
     */
    public int bytesAwaitingWrite() {
        return this.serialPort.bytesAwaitingWrite();
    }

    /**
     * 从串口读取数据
     * @param msg
     * @return
     */
    public int read(byte[] msg) {
        if(this.listener != null) {
            throw new SerialProtocolException("已经使用异步监听则不允许使用此方法");
        }

        return this.serialPort.readBytes(msg, msg.length);
    }

    /**
     * 同步从串口读取数据, 必须读取填充所有数据{@code msg}或者超时才会返回
     * @param msg
     * @param timeout 毫秒
     * @return
     */
    public int readOfSync(byte[] msg, int timeout) {
        if(this.listener != null) {
            throw new SerialProtocolException("已经使用异步监听则不允许使用此方法");
        }

        this.serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_BLOCKING, timeout, this.serialPort.getWriteTimeout());
        int readBytes = this.serialPort.readBytes(msg, msg.length);
        this.serialPort.setComPortTimeouts(SerialPort.TIMEOUT_NONBLOCKING, 0, this.serialPort.getWriteTimeout());
        return readBytes;
    }

    /**
     * 从串口读取数据
     * @param msg
     * @return
     */
    public int read(byte[] msg, int offset) {
        if(this.listener != null) {
            throw new SerialProtocolException("已经使用异步监听则不允许使用此方法");
        }

        return this.serialPort.readBytes(msg, msg.length, offset);
    }

    /**
     * 写数据到串口
     * @param msg
     * @return
     */
    public int write(byte[] msg) {
        return this.serialPort.writeBytes(msg, msg.length);
    }

    /**
     * 阻塞等待写完
     * @param msg
     * @param timeout 毫秒
     * @return
     */
    public int writeOfSync(byte[] msg, int timeout) {
        this.serialPort.setComPortTimeouts(SerialPort.TIMEOUT_WRITE_BLOCKING, this.serialPort.getReadTimeout(), timeout);
        int writeBytes = this.serialPort.writeBytes(msg, msg.length);
        this.serialPort.setComPortTimeouts(SerialPort.TIMEOUT_NONBLOCKING, this.serialPort.getReadTimeout(), 0);
        return writeBytes;
    }

    /**
     * 写数据到串口
     * @param msg
     * @return
     */
    public int write(byte[] msg, int offset) {
        return this.serialPort.writeBytes(msg, msg.length - offset, offset);
    }

    @Override
    public Boolean connect(Consumer<?> consumer, long timeout) {
        boolean openPort = this.serialPort.openPort(this.connectProperties.getSafetySleepTime()
                , this.connectProperties.getSendDeviceQueueSize()
                , this.connectProperties.getReceiveDeviceQueueSize());
        if(consumer != null) {
            ((Consumer<Boolean>) consumer).accept(openPort);
        } else {
            if(openPort) {
                if(logger.isInfoEnabled()) {
                    logger.info("串口客户端 打开串口成功 - 串口：{}", this.connectProperties);
                }
            } else {
                logger.error("串口客户端 打开串口失败 - 串口：{}", this.connectProperties);
            }
        }

        return openPort;
    }

    /**
     * 增加数据监听
     * @param dataListener
     * @return
     */
    public synchronized boolean addDataListener(SerialPortDataListener dataListener) {
        if(this.getListener() == null) {
            this.listener = dataListener;
            return this.getSerialPort().addDataListener(dataListener);
        } else {
            return false;
        }
    }

    /**
     * 移除监听
     * @return
     */
    public synchronized void removeDataListener() {
        if(this.getListener() != null) {
            this.listener = null;
            this.getSerialPort().removeDataListener();
        }
    }

    /**
     * 关闭串口
     * @param remove 是否移除 如果{@code true}将直接移除此连接
     * @return
     */
    @Override
    public Boolean disconnect(boolean remove) {
        if(remove) {
            this.getClientComponent().removeClient(this.getConnectProperties());
        }

        return this.serialPort.closePort();
    }

    @Override
    public ClientComponent getClientComponent() {
        return this.serialComponent;
    }

    public SerialPort getSerialPort() {
        return serialPort;
    }

    public SerialConnectProperties getConnectProperties() {
        return connectProperties;
    }

    public SerialPortDataListener getListener() {
        return listener;
    }
}
